var _ = require('lodash');
var FlowNode = require('../modules/FlowNode');
var FlowEdge = require('../modules/FlowEdge');
var fv = null;
var clickTimeout = null;
module.exports = Interactions;
module.exports.Types = [
    'Node',
    'Drag',
    'EdgeRegion',
    'NewEdge',
    'CreateEdge'
];
function Interactions(flowviz) {
    fv = flowviz;
    this._interactions = [];
    this._supers = [];
    // Defaults
    this.SetDefault("Node", "click", SelectOrRemoveNode);
    this.SetDefault("Node", "dblclick", StartConn);
    this.SetDefault("Drag", "drag", MoveNode);
    this.SetDefault("EdgeRegion", "click", SelectOrRemoveEdge);
    //this.SetDefault("EdgeRegion", "dblclick", ChangeEdgeDirection);
    this.SetDefault("NewEdge", "mousemove", UpdateNewEdge);
    this.SetDefault("NewEdge", "click", CancelEdge);
    this.SetDefault("CreateEdge", "click", FinishEdge);
    // User overrides
    var that = this;
    _.forEach(fv.Config.Interactions, function(interaction) {
       // TODO: do something to set user defined interactions
        console.log("adding interaction");
        that.SetInteraction(interaction.name, interaction.event, interaction.func);
    });
}
Interactions.prototype.SetupInteractions = function(selection, interactions) {
    _.forEach(fv.Interactions._interactions, function (int) {
        if (_.includes(interactions, int.name)) {
            selection.on(int.event, int.func);
        }
    });
};
Interactions.prototype.Clear = function(selection, interactions) {
    _.forEach(fv.Interactions._interactions, function (int) {
        if (_.includes(interactions, int.name)) {
            selection.on(int.event, null);
        }
    });
};
Interactions.prototype.SetDefault = function(name, domEvt, func) {
    var index = _.findIndex(this._interactions, function(i) {
        return i.name === name && i.event === domEvt;
    });
    if(index < 0) {
        this._interactions.push({
            name: name,
            event: domEvt,
            func: func
        });
        this._supers.push({
            name: name,
            event: domEvt,
            func: func
        });
        return;
    }
    throw new Error("You can't set more than one default interaction!");
};
Interactions.prototype.RunSuper = function(name, event, thisArg) {
    var index = _.findIndex(this._supers, function(i) {
        return i.name === name && i.event === event;
    });
    this._supers[index].func.apply(thisArg, _.slice(arguments, 3));
};
Interactions.prototype.SetInteraction = function(name, domEvt, func) {
    var index = _.findIndex(this._interactions, function(i) {
        return i.name === name && i.event === domEvt;
    });
    if(index < 0) {
        this._interactions.push({
            name: name,
            event: domEvt,
            func: func
        });
    } else {
        this._interactions[index].func = func;
    }
};
Interactions.prototype.GetDomEvent = function(name) {
    var index = _.findIndex(this._interactions, function(i) {
        return i.name === name;
    });
    if(index >= 0) {
        return this._interactions[index].event;
    }
    throw new Error(name + " is not a registered interaction!");
};
Interactions.prototype.GetFunc = function(name) {
    var index = _.findIndex(this._interactions, function(i) {
        return i.name === name;
    });
    if(index >= 0) {
        return this._interactions[index].func;
    }
    throw new Error(name + " is not a registered interaction!");
};
function SelectOrRemoveNode(d, i) {
    // When there is a selected node, stop the click propagation
    if(fv.Selection.Current !== null) {
        d3.event.stopPropagation();
    }
    // Dragging shouldn't change a selection
    if (d3.event.defaultPrevented) return;
    // If ctrl is down, remove the node, otherwise, select it
    if (d3.event.ctrlKey) {
        fv.Renderer.Reinitialize();
        fv.GraphManager.RemoveNode(d);
    } else {
        d3.event.stopPropagation();
        if(clickTimeout === null) {
            clickTimeout = setTimeout(function (data, that) {
                fv.Selection.UpdateSelection(data, d3.select(that));
                clickTimeout = null;
            }, 200, d, this);
        }
    }
}
function ChangeEdgeDirection(d, i) {
    //d3.event.sourceEvent.stopPropagation();
    d.direction = (d.direction + 1) % FlowEdge.TOTAL_DIRS;
    fv.Renderer.RedrawEdges();
}
function MoveNode(d, i) {
    fv.GraphManager.MoveNode(d, d.x = d3.event.x, d.y = d3.event.y);
}
function SelectOrRemoveEdge(d, i) {
    d3.event.stopPropagation();
    if (d3.event.ctrlKey) {
        fv.Renderer.Reinitialize();
        fv.GraphManager.RemoveEdge(d);
    } else {
        fv.Selection.SelectEdge(d);
    }
}
function StartConn(d, i) {
    if(clickTimeout !== null) {
        clearTimeout(clickTimeout);
        clickTimeout = null;
    }
    fv.Selection.Clear(true);
    fv.GraphManager.StartConnection(d, d3.event.x, d3.event.y);
}
function UpdateNewEdge(evt) {
    d3.select('g.flow-node-temp')
        .attr('transform', function(d) {
            d.x = evt.offsetX;
            d.y = evt.offsetY;
            return 'translate(' + d.x + ',' + d.y + ')';
        });
    d3.select('g.flow-edge-temp')
        .each(function(d) {
            d.Update();
        })
        .select('path')
        .attr('d', function(d) {
            return fv.Renderer.DrawLine(d.getPath());
        });
}
function FinishEdge(d, i) {
    fv.GraphManager.EndConnection(d);
    fv.Renderer.RemoveTempListeners();
}
function CancelEdge() {
    fv.GraphManager.EndConnection(null);
    fv.Renderer.RemoveTempListeners();
}